// @algorithm @lc id=65 lang=cpp 
// @title valid-number


// @test("0")=true
// @test("e")=false
// @test(".")=false
// @test(".1")=true

/*作者：LeetCode-Solution
链接：https://leetcode-cn.com/problems/valid-number/solution/you-xiao-shu-zi-by-leetcode-solution-298l/
来源：力扣（LeetCode）
著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。*/
class Solution {
public:
    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,         // 符号位
        STATE_INTEGER,          // 整数部分
        STATE_POINT,            // 小数点（左有整数）
        STATE_POINT_WITHOUT_INT,// 小数点（左无整数）
        STATE_FRACTION,         // 小数部分
        STATE_EXP,              // 字符 e
        STATE_EXP_SIGN,         // 指数符号
        STATE_EXP_NUMBER,       // 指数数字
    };

    enum CharType {
        CHAR_NUMBER,            // 0-9
        CHAR_EXP,               // eE
        CHAR_POINT,             // .
        CHAR_SIGN,              // +-
        CHAR_ILLEGAL            // 非法字符
    };

    CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CHAR_EXP;
        } else if (ch == '.') {
            return CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CHAR_SIGN;
        } else {
            return CHAR_ILLEGAL;
        }
    }

    inline static
    unordered_map<State, unordered_map<CharType, State>> transfer =
    {
        {
            STATE_INITIAL, {
                {CHAR_NUMBER, STATE_INTEGER},
                {CHAR_POINT, STATE_POINT_WITHOUT_INT},
                {CHAR_SIGN, STATE_INT_SIGN}
            }
        }, {
            STATE_INT_SIGN, {
                {CHAR_NUMBER, STATE_INTEGER},
                {CHAR_POINT, STATE_POINT_WITHOUT_INT}
            }
        }, {
            STATE_INTEGER, {
                {CHAR_NUMBER, STATE_INTEGER},
                {CHAR_EXP, STATE_EXP},
                {CHAR_POINT, STATE_POINT}
            }
        }, {
            STATE_POINT, {
                {CHAR_NUMBER, STATE_FRACTION},
                {CHAR_EXP, STATE_EXP}
            }
        }, {
            STATE_POINT_WITHOUT_INT, {
                {CHAR_NUMBER, STATE_FRACTION}
            }
        }, {
            STATE_FRACTION,
            {
                {CHAR_NUMBER, STATE_FRACTION},
                {CHAR_EXP, STATE_EXP}
            }
        }, {
            STATE_EXP,
            {
                {CHAR_NUMBER, STATE_EXP_NUMBER},
                {CHAR_SIGN, STATE_EXP_SIGN}
            }
        }, {
            STATE_EXP_SIGN, {
                {CHAR_NUMBER, STATE_EXP_NUMBER}
            }
        }, {
            STATE_EXP_NUMBER, {
                {CHAR_NUMBER, STATE_EXP_NUMBER}
            }
        }
    };

    bool isNumber(string s) {
        int len = s.length();
        State st = STATE_INITIAL;

        for (int i = 0; i < len; i++) {
            CharType typ = toCharType(s[i]);
            if (transfer[st].find(typ) == transfer[st].end()) {
                return false;
            } else {
                st = transfer[st][typ];
            }
        }
        return st == STATE_INTEGER || st == STATE_POINT || st == STATE_FRACTION || st == STATE_EXP_NUMBER;
    }
};